﻿// 
//    Copyright 2010 The JaernCloud Project
// 
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
// 
//        http://www.apache.org/licenses/LICENSE-2.0
// 
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
// 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using JaernCloud.Language.Compiler.Types.Builtin.View;
using JaernCloud.Web.Controllers;
using System.Web.Mvc;
using JaernCloud.Language.Compiler.Types;
using JaernCloud.Language.Runtime;
using JaernCloud.Language.Runtime.View;
using System.IO;
using System.Web.UI;

namespace JaernCloud.Web.Ajax {
    
    /// <summary>
    /// Ajax handler for ajax actions.
    /// </summary>
    public class JaernCloudAjaxAction {

        public JaernCloudAjaxAction(JaernCloudController controller) {
            this.Controller = controller;
        }

        public JsonResult ProcessAjaxAction(IViewNodeType viewType, JSONString request) {
        
            // TODO: Performance optimize ajax requests - only work on a partial tree

            // locate the action node
            string actionSelector = (string)request.JSON["action"];
            IActionNodeType action = new NodeSelector().Single<IActionNodeType>(viewType, actionSelector);
            if (action == null) {
                throw new UnknownIdentifierException(actionSelector, viewType.SourceNode);
            }

            // run the action (find the owner and create it's a node instance for it
            INodeType parent = action.Parent;
            IActionNodeTypeOwner actionOwner = null;
            while (parent != null) {
                if (parent is IActionNodeTypeOwner) {
                    actionOwner = parent as IActionNodeTypeOwner;
                    break;
                }
                parent = parent.Parent;
            }
            INodeObject<IActionNodeTypeOwner> actionOwnerInstance = (INodeObject<IActionNodeTypeOwner>)Controller.Generator.GetNodeObjectInstanceFor(actionOwner);
            StringBuilder sb = new StringBuilder();
            List<INodeObject<INodeType>> expressionResultNodes = new List<INodeObject<INodeType>>();
            foreach (var child in actionOwnerInstance.Children) {
                if (child.Type.Equals(action)) {
                    INodeObject<INodeType> actionInstance = child as INodeObject<INodeType>;
                    foreach (ExpressionNodeObject expression in actionInstance.Children) {
                        // evaluate expression in the action
                        var value = expression.ExpressionValue;
                        if (value is INodeObject<INodeType>) {
                            // the expression result is a node
                            expressionResultNodes.Add(value as INodeObject<INodeType>);
                        }
                        int count = value;
                        sb.Append(count).Append(' ');
                    }
                    break;
                }
            }

            // determine action node dependencies
            List<INodeType> expressionTypes = new List<INodeType>();
            foreach (var expressionNode in expressionResultNodes) {
                var node = expressionNode;
                while (node != null) {
                    expressionTypes.Add(node.Type);
                    if (node.Parent != null) {
                        node = node.Parent;
                    } else if (node is IHasValueHolder) {
                        node = (node as IHasValueHolder).ValueHolder;
                    } else {
                        break;
                    }
                }
            }

            // render dependant nodes
            List<INodeType> references = new List<INodeType>();
            foreach (var expressionType in expressionTypes) {
                references.AddRange(NodeTypeHelper.FindReferencesTo(viewType, expressionType, typeof(IRenderNodeType), typeof(IExpressionNodeType)));
            }
            references.RemoveAll(r => r.Parent is IActionNodeType); // references from actions's can be ignored for rendering

            var ajaxIdToRendered = new Dictionary<string, string>();
            foreach (var reference in references) {
                var nodeType = reference;
                while (nodeType != null && !(nodeType is IRenderNodeType)) {
                    // find the nearest node to render
                    nodeType = nodeType.Parent;
                }
                if (nodeType != null && nodeType is IRenderNodeType) {
                    // for now assume we're rendering in HTML
                    var instance = Controller.Generator.GetNodeObjectInstanceFor(nodeType);
                    IRenderNodeObject renderInstance = instance as IRenderNodeObject;
                    StringWriter writer = new StringWriter();
                    HtmlTextWriter htmlWriter = new HtmlTextWriter(writer);
                    renderInstance.Render(instance, htmlWriter);
                    ajaxIdToRendered[renderInstance.RenderId] = writer.ToString();
                }
            }

            var data = new {
                render = ajaxIdToRendered
            };
            JsonResult result = new JsonResult();
            result.Data = data;
            return result;

        }

        public JaernCloudController Controller { get; private set; }
    }
}
